#ifndef DIAGNOSIS_SHARED_MEMORY_H
#define DIAGNOSIS_SHARED_MEMORY_H

#include <iostream>
#include <string>
#include <atomic>
#include <memory>
#include <cstring>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <unordered_map>

#define RED "\033[31m"
#define RESETS "\033[0m"

static const std::unordered_map<std::string, int> nodes_map =
    {
        // 算法和业务相关模块中的节点
        {"nav_simulaiton", 1},
        {"test2", 2}};

enum class eShmAreaStatus
{
    IDLE = 0,
    WRITING,
    READING,
    UPDATED
};

struct AreaLocation
{
    int array_index;
    long long seq_num;

    AreaLocation()
    {
        array_index = -1;
        seq_num = -1;
    }
};

struct ShmHeader
{
    std::atomic<int> shm_attach_times;
    std::atomic<int> buffer_len;
    AreaLocation lowest_loc;
    AreaLocation highest_loc;

    ShmHeader()
    {
        shm_attach_times.store(0);
        buffer_len.store(-1);
    }

    // friend std::ostream &operator<<(std::ostream &output, const ShmHeader &src);
};
struct HeartBeatInfo
{
    double beat_time;
    int beat_count;

    HeartBeatInfo()
    {
        beat_time = 0;
        beat_count = 0;
    }
};
struct AlarmEventInfo
{
    int event_code;
    std::string add_info;
    bool cancel_trigger;

    AlarmEventInfo()
    {
        event_code = 0;
        add_info = "";
        cancel_trigger = false;
    }
};

struct SharedMemoryData
{
    AlarmEventInfo alarm_info;
    HeartBeatInfo heartbeat_info;
};

struct ShmAreaHeader
{
    std::atomic<eShmAreaStatus> shm_status;
    std::atomic<long long> sequence;
    std::atomic<double> send_time_stamp;

    ShmAreaHeader()
    {
        shm_status.store(eShmAreaStatus::IDLE);
        sequence.store(0);
        send_time_stamp.store(0.0);
    }
};

struct ShmInfo
{
    int shm_nattch;        // 使用该内存的进程的个数
    long last_attach_time; // 最后一个进程链接到该内存段的时间

    ShmInfo()
    {
        shm_nattch = 0;
        last_attach_time = 0;
    }

    // friend std::ostream &operator<<(std::ostream &output, const ShmInfo &src);
};

struct ShmID
{
    size_t shmkey;
    unsigned int shmbytes;
    int shmid;

    ShmID()
    {
        shmkey = 0;
        shmbytes = 0;
        shmid = 0;
    }

    // friend std::ostream &operator<<(std::ostream &output, const ShmID &src);
};

class SharedMemory
{
public:
    SharedMemory() {};
    ~SharedMemory() {};

    SharedMemoryData *shared_data;
    int _shmid;
    bool is_shmidok;
    std::string _nodename;
    /***
     * @description: 创建及反馈共享内存
     * @param {*}
     * @return {*}
     */
    int Init(std::string nodename)
    {
        _nodename = nodename;
        bool findok = false;
        is_shmidok = false;
        for (const auto &node : nodes_map)
        {
            if (node.first == nodename)
            {
                findok = true;
                break;
            }
        }
        if (!findok)
        {
            std::cout << RED << " node name is not in node_map,please check the putin name!!" << RESETS << std::endl;
            return -1;
        }

        int _shmid = SharedMemoryCreateOrGet(BKDRHash(nodename.c_str()), (sizeof(SharedMemoryData) / 4096 + 1) * 4096);

        if (_shmid == -1)
        {
            std::cout << RED << " Failed to create shared memory!!" << RESETS << std::endl;
            return -1;
        }
        is_shmidok = true;
        std::cout << "shared memory created nodename:" << nodename << ", shmid:" << _shmid << std::endl;

        shared_data = (SharedMemoryData *)SharedMemoryAttach(_shmid);

        return 0;
    }

    /***
     * @description: 数据更新
     * @param {*}
     * @return {*}
     */
    int HeartBeatUpdata()
    {
        if (!is_shmidok)
        {
            std::cout << "error in shared memory name:" << _nodename << std::endl;
            return -1;
        }
        SharedMemoryLock(_shmid);
        shared_data->heartbeat_info.beat_time = ros::Time::now().toNSec();
        if (shared_data->heartbeat_info.beat_count++ > 256)
        {
            shared_data->heartbeat_info.beat_count = 0;
        }
        SharedMemoryUnlock(_shmid);
        return 0;
    }

    /***
     * @description: 字符串hash算法，使用100000个不同字符串产生的冲突数，大概在0~3波动
     * @param {*}
     * @return {*}
     */
    static unsigned int BKDRHash(const char *str)
    {
        unsigned int seed = 131313; // 也可以乘以31、131、1313、13131、131313..
        unsigned int hash = 0;

        while (*str)
        {
            hash = hash * seed + (*str++);
        }

        return (hash & 0x7FFFFFFF); //&0x7FFFFFFF的目的是为了保证hash值是正的
    }

    /***
     * @Author:
     * @description: 获取key指定的共享内存
     * @param {*}
     * @return {*}
     */
    static int SharedMemoryGet(key_t key, size_t size)
    {
        int shmid;

        if ((shmid = shmget(key, size, 06666)) == -1)
        {
            return -1;
        }

        return shmid;
    }

    /***
     * @Author:
     * @description: 创建新的共享内存
     * @param {*}
     * @return {*}
     */
    static int SharedMemoryCreate(key_t key, size_t size)
    {
        int shmid;

        if ((shmid = shmget(key, size, 0666 | IPC_CREAT)) == -1)
        {
            return -1;
        }

        return shmid;
    }

    /***
     * @Author:
     * @description: 如果存在对应内存则获取该共享内存，否则创建新共享内存
     * @param {*}
     * @return {*}
     */
    static int SharedMemoryCreateOrGet(key_t key, size_t size)
    {
        int shmid;

        // IPC_CREAT和IPC_EXCL同时使用时，如果该共享内存已存在则返回-1
        if ((shmid = shmget(key, size, 0666 | IPC_CREAT | IPC_EXCL)) == -1)
        {
            if (errno != EEXIST)
                return -1;

            if ((shmid = shmget(key, size, 0666)) == -1)
                return -1;
        }

        return shmid;
    }

    /***
     * @Author:
     * @description: 共享内存销毁后重新创建，使用同一个key
     * @param {*}
     * @return {*}
     */
    static int SharedMemoryCreateIfGone(key_t key, size_t size)
    {
        int shmid;

        if ((shmid = shmget(key, size, 0666 | IPC_CREAT | IPC_EXCL)) == -1)
        {
            return errno;
        }

        return shmid;
    }

    /***
     * @Author:
     * @description: 把共享内存绑定到进程
     * @param {*}
     * @return {*}返回指向共享内存第一个字节的指针
     */
    static void *SharedMemoryAttach(int shmid)
    {
        return shmat(shmid, 0, 0);
    }

    /***
     * @Author:
     * @description: 分离共享内存与进程
     * @param {*}
     * @return {*}
     */
    static int SharedMemoryDetach(const void *shmaddr)
    {
        if (shmdt(shmaddr) == -1)
            return errno;

        return 0;
    }

    /***
     * @Author:
     * @description: 获取共享内存信息
     * @param {*}
     * @return {*}
     */
    static int SharedMemoryInfo(int shmid, ShmInfo &info)
    {
        struct shmid_ds ds;

        if (shmctl(shmid, IPC_STAT, &ds) == -1)
            return errno;

        info.shm_nattch = ds.shm_nattch;
        info.last_attach_time = ds.shm_atime;

        return 0;
    }

    /***
     * @Author:
     * @description: 删除共享内存
     * @param {*}
     * @return {*}
     */
    static int SharedMemoryDelete(int shmid)
    {
        if (shmctl(shmid, IPC_RMID, 0) == -1)
            return errno;

        return 0;
    }

    /***
     * @Author:
     * @description: 给共享内存上锁
     * @param {*}
     * @return {*}
     */
    static int SharedMemoryLock(int shmid)
    {
        if (shmctl(shmid, SHM_LOCK, 0) == -1)
            return errno;

        return 0;
    }

    /***
     * @Author:
     * @description: 给共享内存解锁
     * @param {*}
     * @return {*}
     */
    static int SharedMemoryUnlock(int shmid)
    {
        if (shmctl(shmid, SHM_UNLOCK, 0) == -1)
            return errno;

        return 0;
    }

    /***
     * @Author:
     * @description: 重新分配共享内存空间
     * @param {*}
     * @return {*}
     */
    static int ResizeSharedMemory(const int shmid, const int key, const int size_bak, const int target_size)
    {
        char *bak_data = new char[size_bak];
        char *from_mem = static_cast<char *>(SharedMemoryAttach(shmid));
        memcpy(bak_data, from_mem, size_bak);
        SharedMemoryDelete(shmid);

        int target_shmid = SharedMemoryCreateOrGet(key, target_size);
        char *to_mem = static_cast<char *>(SharedMemoryAttach(target_shmid));
        memcpy(to_mem, bak_data, size_bak);

        delete[] bak_data;

        return target_shmid;
    }
};

#endif